package com.tianji.promotion.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.autoconfigure.redisson.annotations.Lock;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.ExchangeCode;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.query.UserCouponQuery;
import com.tianji.promotion.domain.vo.CouponVO;
import com.tianji.promotion.enums.ExchangeCodeStatus;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.mapper.UserCouponMapper;
import com.tianji.promotion.service.ICouponService;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import com.tianji.promotion.utils.CodeUtil;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

import static com.tianji.common.constants.CacheConstant.EXCHANGE_CODE_BITMAP;

/**
 * <p>
 * 用户领取优惠券的记录，是真正使用的优惠券信息 服务实现类
 * </p>
 *
 * @author xjs
 */
// @Service
public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {

    @Autowired
    @Lazy
    private ICouponService couponService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private IExchangeCodeService codeService;
    @Autowired
    private CouponMapper couponMapper;
    @Autowired
    private PlatformTransactionManager txManager;
    /**
     * this调用导致事务失效解决方案1：
     * 自己注入自己，@Lazy循环依赖
     * */
    @Autowired
    @Lazy
    private  IUserCouponService userCouponService;

    @Override
    public PageDTO<CouponVO> queryMyCouponPage(UserCouponQuery query) {
        return null;
    }

    @Override
    public void exchangeCoupon(String code) {
        Long userId = UserContext.getUser();
        // 解析code， CodeUtil
        long serialNum = CodeUtil.parseCode(code);
        // 判断是否已经兑换过了,Bitmap
        Boolean exists = redisTemplate.opsForValue().setBit(EXCHANGE_CODE_BITMAP, serialNum, true);
        if(exists){
            // 是，return
            throw new BizIllegalException("不能重复兑换");
        }
        // 查询兑换码
        ExchangeCode exchangeCode = codeService.lambdaQuery().eq(ExchangeCode::getCode, code).one();
        // 如果不存在，return
        if(exchangeCode == null){
            throw new BizIllegalException("兑换码不存在");
        }
        Long couponId = exchangeCode.getExchangeTargetId();
        // 判断是否过期
        LocalDateTime now = LocalDateTime.now();
        if(exchangeCode.getExpiredTime().isBefore(now)){
            // 如果已经过期，return
            throw new BizIllegalException("兑换码已经过期");
        }
        // 查询coupon
        Coupon coupon = couponService.getById(couponId);
        if(coupon == null){
            throw new BizIllegalException("优惠券不存在");
        }
        // 判断是否超出限领数量，查询user_coupon
        //   查询用户已经领取的券的数量
        Integer count = this.lambdaQuery()
                .eq(UserCoupon::getUserId, userId)
                .eq(UserCoupon::getCouponId, couponId)
                .count();
        if(count >= coupon.getUserLimit()){
            // 如果超出，return
            throw new BizIllegalException("优惠券超出每人限领数量");
        }
        // update coupon set issue_num = issue_num+1 where id = #{couponId}
        couponService.lambdaUpdate().setSql("issue_num = issue_num + 1")
                .eq(Coupon::getId, couponId).update();
        // insert user_coupon
        UserCoupon uc= new UserCoupon();
        uc.setUserId(userId);
        uc.setCouponId(couponId);
        if(coupon.getTermDays() != null && coupon.getTermDays() > 0){
            uc.setTermBeginTime(LocalDateTime.now());
            uc.setTermEndTime(LocalDateTime.now().plusDays(coupon.getTermDays()));
        }else{
            uc.setTermBeginTime(coupon.getTermBeginTime());
            uc.setTermEndTime(coupon.getTermEndTime());
        }
        uc.setStatus(1);
        uc.setCreateTime(LocalDateTime.now());
        uc.setUpdateTime(LocalDateTime.now());
        this.save(uc);
        // update exchange_code set user_id = #{userId}, status = 2
        codeService.lambdaUpdate().set(ExchangeCode::getUserId, userId)
                .set(ExchangeCode::getStatus, ExchangeCodeStatus.USED.getValue())
                .eq(ExchangeCode::getId, exchangeCode.getId())
                .update();
    }

//    @Autowired
//    RedisLock redisLock;

    @Autowired
    private RedissonClient redissonClient;

    // Spel表达式
    @Override
    @Lock(name="redisson-lock:#{couponId}")
    // @MyLock(name="test-redison-lock:", leaseTime = 10,timeUnit = TimeUnit.SECONDS)
    // @Transactional(rollbackFor = Exception.class)     // 对业务方法做了一个代理
    public void receive(Long couponId) {                // 执行业务方法之前，开启事务
                                                         // try{
                                                         //      receive(couponId)
                                                         //    提交事务
                                                          // }
                                                         // catch（e）{
                                                          //        回滚事务
                                                          // }
        // 获取用户id
        Long userId = UserContext.getUser();
        // 根据couponId查询coupon
        Coupon coupon = couponService.getById(couponId);
        // coupon不存在，return
        if(coupon == null){
            throw new BizIllegalException("优惠券不存在");
        }
        // 判断库存是否充足，issue_num《 total_num
        Integer issueNum = coupon.getIssueNum();
        Integer totalNum = coupon.getTotalNum();
        if(issueNum >= totalNum){
            // 没有库存，return
            throw new BizIllegalException("优惠券库存不足");
        }
        // couponIdString指的是String常量池中的对象
        String couponIdString = couponId.toString().intern();
        // 单机锁，分布式环境无效
//        synchronized (couponIdString){
//            IUserCouponService proxy = (IUserCouponService) AopContext.currentProxy();
//            proxy.checkAndCreateUserCoupon(userId, coupon);
//        }
//        // 创建锁对象
//        RLock lock = redissonClient.getLock(couponIdString);
//        boolean locked = false;
//        try{
//            // 尝试获取锁
//            locked = lock.tryLock(0, 10, TimeUnit.SECONDS);
//            // this调用导致事务失效解决方案2：当前的代理对象放到AopContext中
//            // 获取当前的代理对象
//            if(locked){
                // 获取锁成功，执行业务逻辑
                IUserCouponService proxy = (IUserCouponService) AopContext.currentProxy();
                proxy.checkAndCreateUserCoupon(userId, coupon);
//            }else{
//                //
//            }
//        }catch (Exception e){
//            throw new BizIllegalException("获取分布式锁异常");
//        }finally {
//            // 释放锁
//            if(locked){
//                lock.unlock();
//            }
//        }
        //}
        // commit();
    }

    @Transactional(rollbackFor = Exception.class)
    public void checkAndCreateUserCoupon(Long userId, Coupon coupon){
        Long couponId = coupon.getId();
        //是否超出限领数量
        Integer count = this.lambdaQuery()
                .eq(UserCoupon::getUserId, userId)
                .eq(UserCoupon::getCouponId, couponId)
                .count();
        if(count >= coupon.getUserLimit()){
            // 如果超出，return
            throw new BizIllegalException("优惠券超出每人限领数量");
        }
        // 更新coupon的issue_num
//        coupon.setIssueNum(coupon.getIssueNum()+1);
//        couponService.updateById(coupon);
        // 乐观锁：version
        // select issue_num,total_num, version from coupon where coupon_id = #{couponId}
        // if(issue_num < total_num){
        //       UPDATE coupon SET issue_num = issue_num + 1 WHERE id = ? and issue_num < total_num
        // }
//        couponService.lambdaUpdate().setSql("issue_num = issue_num + 1")
//                .eq(Coupon::getId, couponId)
//                .update();

        int result = couponMapper.updateIssueNum(couponId);
        // 影响记录的行数，只有>0说明是更新成功了
        if(result > 0){
            // insert到user_coupon
            UserCoupon uc= new UserCoupon();
            uc.setUserId(userId);
            uc.setCouponId(couponId);
            if(coupon.getTermDays() != null && coupon.getTermDays() > 0){
                uc.setTermBeginTime(LocalDateTime.now());
                uc.setTermEndTime(LocalDateTime.now().plusDays(coupon.getTermDays()));
            }else{
                uc.setTermBeginTime(coupon.getTermBeginTime());
                uc.setTermEndTime(coupon.getTermEndTime());
            }
            uc.setStatus(1);
            uc.setCreateTime(LocalDateTime.now());
            uc.setUpdateTime(LocalDateTime.now());
            this.save(uc);
        }
    }
}
